package com.jule.teenpatti.game.gameUtil;


import com.jule.core.jedis.StoredObjManager;
import com.jule.teenpatti.base.enums.AlarmEnum;
import com.jule.teenpatti.base.enums.GameConst;
import com.jule.teenpatti.base.enums.RedisConst;
import com.jule.teenpatti.base.enums.RoleType;
import com.jule.teenpatti.game.model.eenum.NextBetPlayerReasonEnum;
import com.jule.teenpatti.game.network.protocol.logic.LeaveTableLogic;
import com.jule.teenpatti.game.play.TableUtil;
import com.jule.teenpatti.game.service.*;
import com.jule.teenpatti.game.service.holder.CardOfTableHolder;
import com.jule.teenpatti.game.utils.NumberUtils;
import com.jule.core.common.log.LoggerUtils;
import com.jule.teenpatti.game.config.Config;
import com.jule.teenpatti.game.model.PlayerInfo;
import com.jule.teenpatti.game.model.eenum.PlayTypeEnum;
import com.jule.teenpatti.game.model.eenum.PlayerStateEnum;
import com.jule.teenpatti.base.enums.TableStateEnum;
import com.jule.teenpatti.game.network.protocol.TableInnerReq;
import com.jule.teenpatti.game.play.AbstractTable;
import com.jule.teenpatti.game.service.holder.FunctionIdHolder;
import com.jule.teenpatti.game.utils.log.TableLogUtil;
import com.jule.teenpatti.game.vavle.notice.NoticeBroadcastMessages;
import com.jule.teenpatti.log.service.LogReasons;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

@Slf4j
public class GameLogic {

    /**
     * 游戏开始计时器
     *
     * @param table
     */
    public static void gameStartTimer(final AbstractTable table) {
        //停止牌桌内正在进行的倒计时
        TimerService.getInstance().delTimerTask(table.getRoomTableRelation());
//        log.debug("游戏开始计时器开始计时,倒计时秒数->" + table.getCommonConfig().getGameStartCountDownSec() + ", dealerSeatNum->" + table.getCurrDealerSeatNum());
        TimerService.getInstance().addTimerTask(table.getCommonConfig().getGameStartCountDownSec(), table.getRoomTableRelation(), new TableInnerReq(table.getPlayType() + "", table.getRoomId(), table.getTableId()) {
            @Override
            public void processImpl() {
                gameStart(table);
            }
        });
    }

    public static void gameStart(AbstractTable table) {
        //停止牌桌内正在进行的倒计时
        TimerService.getInstance().delTimerTask(table.getRoomTableRelation());

        //暂停一会儿，以免机器人无法入桌
        TimerService.getInstance().addTimerTask(1, table.getRoomTableRelation(), new TableInnerReq(table.getPlayType() + "", table.getRoomId(), table.getTableId()) {
            @Override
            public void processImpl() {
                long start = System.currentTimeMillis();
                try {
                    table.setTableStateEnum(TableStateEnum.GAME_START);
                    table.setTableStatus();

                    table.setLoseUserIds(table.getInGamePlayerIds(""));
                    double firstBetScore = table.getBaseBetScore() / 2;
                    StringBuilder stringBuilder = new StringBuilder();
                    for (PlayerInfo playerInfo : table.getInGamePlayersBySeatNum().values()) {
                        //玩家的钱不够下注则推站起消息
                        log.debug("GameLogic -> 玩家目前的状态值：" + playerInfo.getState());
                        if (playerInfo.isOffLine()
                                || playerInfo.getPlayScoreStore() < firstBetScore
                                || playerInfo.getState() != PlayerStateEnum.prepare) { //modify:2018-07-19 lyb 增加下线判断
                            log.info("开局时，玩家状态错误，因此被站起。isOffLine->{}, isSocreNotEnough->{}, playerState->{}",
                                    playerInfo.isOffLine(), playerInfo.getPlayScoreStore() < firstBetScore, playerInfo.getState());
                            table.standUp(playerInfo.getSeatNum(), playerInfo.getPlayerId(), "gameStartTimer-scoreError or isOffLine:" + playerInfo.isOffLine());
                            if (playerInfo.getRoleType() != null && playerInfo.getRoleType().equals(RoleType.ROBOT)) {
                                LoggerUtils.robot.info("Robot standUp reason gameStart money id:" + playerInfo.getPlayerId() + ",gameId:" + table.getPlayType()
                                        + ",roomId:" + table.getRoomId() + ",tableId:" + table.getTableId());
                            }
                            //广播玩家站起消息
                            NoticeBroadcastMessages.playerStandUp(table, playerInfo.getPlayerId(), playerInfo.getSeatNum());
                            //玩家超时未准备或金额不足 自动离桌 根据外包更改的需求
                            LeaveTableLogic.getInstance().logic(playerInfo, table, null);
                            continue;
                        }
                        table.getInGamePlayers().put(playerInfo.getSeatNum(), playerInfo); //将所有人存入游戏中map
                    }
                    //modify 2018-07-19 lyb 在循环外判断是否重置桌子状态
                    if (table.getInGamePlayersBySeatNum().size() < 2) {
                        stringBuilder.append(" InGamePlayers size: " + table.getInGamePlayers().size());
                        table.getInGamePlayers().forEach((k, v) -> v.setState(PlayerStateEnum.siteDown));
                        table.getInGamePlayers().clear();
                        table.setTableStateEnum(TableStateEnum.IDEL);
                        table.setTableStatus();
                        log.info("gameStartTimer() " + stringBuilder.toString() +
                                " , current InGamePlayers size:" + table.getInGamePlayers().size());
                        return;
                    }
                    TaskService.getInstance().cardNum(table);
                    //设置庄位
                    table.setDealer();

                    //特殊玩法：dealer
                    if (table.getPlayType() == PlayTypeEnum.dealer.getGameId()) {
                        table.setTableStateEnum(TableStateEnum.choosePlayType); //修改牌桌状态为选择玩法中
                        table.setTableStatus();
                        try {
                            NoticeBroadcastMessages.choosePlayTypes(table);
                        } catch (Exception e) {
                            log.debug("选择玩法出现异常");
                        }
                        choosePlayTypeCD(table);
                        return;
                    }

                    PlayerInfo nextBetPlayer = table.getNextBetPlayer(table.getCurrDealerSeatNum(), NextBetPlayerReasonEnum.GAME_LOGIC_START);
                    //记录本局第一个下注玩家座位号
                    if (nextBetPlayer == null) {
                        StoredObjManager.hset(RedisConst.ALARM_CHECK.getProfix() + AlarmEnum.NEXT_BET_PLAYER.getType(),
                                RedisConst.ALARM_CHECK.getField() + AlarmEnum.NEXT_BET_PLAYER.getType(),
                                "IP:" + Config.BIND_IP + "GameId:" + table.getPlayType() + ",RoomId:" + table.getRoomId() + ",TableId:" + table.getTableId());
                        log.error("设置庄位 nextBetPalyer is null " + table.getCurrDealerSeatNum());
                        return;
                    }

                    for (PlayerInfo playerInfo : table.getInGamePlayers().values()) {
                        table.getAlreadyBet().put(playerInfo.getPlayerId(), firstBetScore);
                        playerInfo.setState(PlayerStateEnum.gameing); //修改所有玩家的状态为游戏中
                        //playerInfo.setPlayScoreStore(playerInfo.getPlayScoreStore() - firstBetScore);//发牌之前先下注
                        playerInfo.minusPlayScoreStore(firstBetScore, LogReasons.CommonLogReason.GAME_GIVECARD);
                        playerInfo.setAlreadyBetScore4Round(firstBetScore);
                        playerInfo.setTotalAlreadyBetScore4Hand(firstBetScore);
                        TableLogUtil.gameAnte(0, "gameStart_ant", playerInfo.getPlayerId(), playerInfo.getNickName(),
                                table.getPlayType() + "", table.getRoomId(), table.getTableId(), table.getCurrGameOrderId(),
                                firstBetScore);
                        playerInfo.setCurChipsLose(firstBetScore);
                        //发送通知
                        NoticeBroadcastMessages.moneyChangeNotice(table, playerInfo.getPlayerId(), playerInfo.getPlayScoreStore());
                    }
                    table.setTotalAlreadyBet(firstBetScore * table.getInGamePlayersBySeatNum().size());
                    table.setFirstBetSeatNum(nextBetPlayer.getSeatNum());
                    if (nextBetPlayer != null) {
                        nextBetPlayer.setState(PlayerStateEnum.beting);
                    }
                    table.setTableStateEnum(TableStateEnum.BET); //修改牌桌状态为下注中
                    table.setTableStatus();

                    try {
                        //调用计时器来播放动画(多少人多少秒)
                        NoticeBroadcastMessages.giveCardRoundStart(table, nextBetPlayer);
                        //玩法要求：hukam
                        if (table.currPlayType() == PlayTypeEnum.hukam.getValue()) {
                            //广播此局游戏的癞子牌
                            NoticeBroadcastMessages.mixedCard(table);
                        }
                    } catch (Exception ex) {
                        log.error("Send notice ERROR:" + ex.getMessage());
                    }
                    //第一次下注延时(发牌)
                    commonTimer(table, table.getInGamePlayers().size(), true);

                    //发送开局日志
                    LogService.OBJ.sendGamestartLog(table);
                    LogService.OBJ.sendGameStartPlayerLog(table);
                } catch (Exception ex) {
                    log.error("gameStart ERROR, msg->{}", ex.getMessage(), ex);
                } finally {
                    if(System.currentTimeMillis() - start > 400){
                        log.error("gameStart 执行时间 {} 毫秒", System.currentTimeMillis() - start);
                    }
                }
            }
        });
    }

    /**
     * 下注计时器
     *
     * @param table
     */
    public static void betTimer(final AbstractTable table) {
        //停止牌桌内正在进行的倒计时
        TimerService.getInstance().delTimerTask(table.getRoomTableRelation());
        log.debug("下注计时器开始计时, 倒计时秒数->" + table.getCommonConfig().getBetCountDownSec() + ", 下注用户Id->" + table.getCurrActionPlayerId());
        log.debug("player={}.下注计时器开始计时time={}",table.getCurrActionPlayerId(), String.valueOf(System.currentTimeMillis()));        TimerService.getInstance().addTimerTask(table.getCommonConfig().getBetCountDownSec(), table.getRoomTableRelation(), new TableInnerReq(table.getPlayType() + "", table.getRoomId(), table.getTableId()) {
            @Override
            public void processImpl() throws Exception {
                table.setLastActionTime(System.currentTimeMillis());
                /*
                 * 当没有其它程序触发提前终止计时器时，那么计时器倒计时完成后，将执行下面的自动弃牌逻辑
                 */
                log.info("table currActionPlayerId:" + table.getTableId() + ":" + table.getCurrActionPlayerId());
                PlayerInfo currBetPlayer = table.getPlayer(table.getCurrActionPlayerId());
                PlayerInfo nextBetPlayer = null;
                if (currBetPlayer == null) {
                    table.standUp(table.getCurrActionSeatNum(), table.getCurrActionPlayerId(), "BetTimeOut-noCurrBetUser");

                    //记录站起玩家的信息
                    table.setOfflinePlayerId(table.getCurrActionPlayerId());
                    table.setOfflineSeatNum(table.getCurrActionSeatNum());

                    //获得下一个应下注玩家
                    nextBetPlayer = table.getNextBetPlayer(table.getCurrActionSeatNum(), NextBetPlayerReasonEnum.GAME_LOGIC_BET);

                    NoticeBroadcastMessages.applyFold(table, currBetPlayer, nextBetPlayer);
                    //广播玩家站起消息
                    NoticeBroadcastMessages.playerStandUp(table, table.getOfflinePlayerId(), table.getOfflineSeatNum());

                    if (nextBetPlayer != null) {
                        table.setTableStateEnum(TableStateEnum.BET); //修改桌面状态为下注中
                        table.setTableStatus();

                        //如果桌面只剩两人，则可以把玩家状态修改为可以直接show
                        if (table.getInGamePlayers().size() == 2 && table.getSeeCardNum() == 2) {
                            nextBetPlayer.setIsCanShow(1);
                        }
                        nextBetPlayer.setState(PlayerStateEnum.beting);
                        betTimer(table); //启动下一个玩家的下注计时器
                    } else {
                        log.debug("当前游戏中的人数{}, userId->{}", table.getInGamePlayers().size(), table.getCurrActionPlayerId());
                        settle(table);
                    }
                    return;
                }

                if (currBetPlayer.getState() == PlayerStateEnum.beting) { //倒计时结束玩家还没有下注则强制玩家弃牌
                    currBetPlayer.setState(PlayerStateEnum.fold); //玩家已弃牌
                    table.getInGamePlayers().remove(currBetPlayer.getSeatNum()); //将玩家从游戏中移除
                    //如果玩家已看牌则修改桌内看牌玩家数量
                    if (currBetPlayer.getIsBlind() != 1) {
                        table.setSeeCardNum(table.getSeeCardNum() - 1);

                        //如果弃牌的人是已看牌玩家，则重新判断当前行动玩家是否可以sideshow
                        table.setUpPlayerShowCard();
                    }


//region 注释by gx 20181010 超时不自动离桌。
//                    if (table.standUp(currBetPlayer.getSeatNum(), currBetPlayer.getPlayerId(), "BetTimeOut-HasCurrBetPlayer")) {
//                        if (currBetPlayer.getRoleType() != null && currBetPlayer.getRoleType().equals(RoleType.ROBOT)) {
//                            LoggerUtils.robot.info("Robot standUp reason timeout id:" + currBetPlayer.getPlayerId() + ",gameId:" + table.getPlayType()
//                                    + ",roomId:" + table.getRoomId() + ",tableId:" + table.getTableId());
//                        }
//                        table.playerDataSettlement(currBetPlayer, true);
//                        table.getInGamePlayers().remove(currBetPlayer.getSeatNum());
//                    }
//endregion

                    //累计玩家自动弃牌次数
                    currBetPlayer.setTimeoutDiscard(true);
                    currBetPlayer.setTimeoutDiscardCount(currBetPlayer.getTimeoutDiscardCount() + 1);
                    log.debug("玩家超时未下注，自动弃牌。" + currBetPlayer.playerToString());

                    //玩家超时未下注 自动离桌 根据外包更改的需求
                    //modify by gx 20181010 超时不自动离桌。 LeaveTableLogic.getInstance().logic(currBetPlayer, table, null);

                    TableLogUtil.fold(FunctionIdHolder.Game_Notice_BetRound_Fold, "timeout_fold", currBetPlayer.getPlayerId(), currBetPlayer.getNickName(),
                            table.getPlayType() + "", table.getRoomId(), table.getTableId(), table.getCurrGameOrderId(), 0, table.getCurrRoundId());
                }


                //获得下一个应下注玩家
                nextBetPlayer = table.getNextBetPlayer(currBetPlayer.getSeatNum(), NextBetPlayerReasonEnum.GAME_LOGIC_BET);


                if (null == nextBetPlayer || table.getInGamePlayers().size() < 2) {
                    if (table.getTableStateEnum() != TableStateEnum.SETTLE) {
                        //找不到下一个行动玩家，那么执行结算逻辑
                        settle(table);
                    }
                } else {
                    table.setTableStateEnum(TableStateEnum.BET); //修改桌面状态为下注中
                    table.setTableStatus();

                    //如果桌面只剩两人，则可以直接show
                    if (table.getInGamePlayers().size() == 2 && table.getSeeCardNum() == 2) {
                        nextBetPlayer.setIsCanShow(1);
                    }
                    nextBetPlayer.setState(PlayerStateEnum.beting);
                    betTimer(table); //启动下一个玩家的下注计时器
                }

//                if (currBetPlayer.getState() == PlayerStateEnum.fold) {
//                    //如果玩家超时弃牌则广播玩家弃牌消息
                NoticeBroadcastMessages.applyFold(table, currBetPlayer, nextBetPlayer);
//                } else {
                //发送已下注通知
//                    NoticeBroadcastMessages.betRoundDoBet(table, table.getCurrBetSeqId(), currBetPlayer, nextBetPlayer,true);
//                }
            }
        });
    }

    /**
     * 显示比牌结果倒计时
     *
     * @param table
     */
    public static void SideShowResultCD(final AbstractTable table, PlayerInfo player, PlayerInfo targetPlayer,PlayerInfo winner) {
        //停止牌桌内正在进行的倒计时
        TimerService.getInstance().delTimerTask(table.getRoomTableRelation());
        TimerService.getInstance().addTimerTask(4, table.getRoomTableRelation(), new TableInnerReq(table.getPlayType() + "", table.getRoomId(), table.getTableId()) {
            @Override
            public void processImpl() {
                long start = System.currentTimeMillis();
                try {
                    //根据各种状态判断能否看到自己的牌 所以改成了这样 此处不是代码重复 不能随便动
                    NoticeBroadcastMessages.sideShowBothSides(table, winner.getPlayerId(), player, targetPlayer);
                    NoticeBroadcastMessages.sideShowBothSides(table, winner.getPlayerId(), targetPlayer, player);

                    table.setUpPlayerShowCard();

                    PlayerInfo nextActionPlayer = table.getNextBetPlayer(player.getSeatNum(), NextBetPlayerReasonEnum.SIDE_SHOW);
                    if (nextActionPlayer != null) {
                        if (table.getInGamePlayers().size() == 2 && table.getSeeCardNum() == 2) {
                            nextActionPlayer.setIsCanShow(1);
                        }
                        //设置下一个行动玩家的状态
                        nextActionPlayer.setState(PlayerStateEnum.beting);
                        //如果超过底池上限则不发下一个行动玩家消息
                        if (table.getTotalAlreadyBet() >= table.getMaxPotScore()) {
                            nextActionPlayer = null;
                        }
                    }

                    if (winner != null) {
                        if (winner.getPlayerId().equals(player.getPlayerId())) {
                            //广播事件：广播比牌大小的结果给桌内其它玩家
                            NoticeBroadcastMessages.sideShowResult(table, player, targetPlayer, nextActionPlayer);
                        } else {
                            //广播事件：广播比牌大小的结果给桌内其它玩家
                            NoticeBroadcastMessages.sideShowResult(table, targetPlayer, player, nextActionPlayer);
                        }
                    } else {
                        //如果比牌时双方有人掉线则直接判定对方胜利
                        if (!table.getInGamePlayers().containsKey(player.getSeatNum())) {
                            //广播事件：广播比牌大小的结果给桌内其它玩家
                            NoticeBroadcastMessages.sideShowResult(table, targetPlayer, null, nextActionPlayer);
                        } else if (!table.getInGamePlayers().containsKey(targetPlayer.getSeatNum())) {
                            //广播事件：广播比牌大小的结果给桌内其它玩家
                            NoticeBroadcastMessages.sideShowResult(table, player, null, nextActionPlayer);
                        }
                    }
                    //如果超过底池上限则全部开牌
                    if (table.getTotalAlreadyBet() >= table.getMaxPotScore()) {
                        //广播提示玩家总分超过桌子上线
                        NoticeBroadcastMessages.settlePrompt(table);
                        //调用定时器来延迟结算时间
                        GameLogic.commonTimer(table, 2, false);
                    } else {
                        //开始下一个玩家的下注倒计时
                        betTimer(table);
                    }
                }catch (Exception ex){
                    log.error("SideShowResultCD ERROR, msg->{}", ex, ex);
                }finally {
                    if(System.currentTimeMillis() - start > 10){
                        log.error("SideShowResultCD 执行时间 {} 毫秒", System.currentTimeMillis() - start);
                    }
                }
            }
        });
    }

    public static PlayerInfo getSideShowResult(AbstractTable table, PlayerInfo player, PlayerInfo targetPlayer){
        if (player.getIsBlind() == 1) {
            //根据玩法的不同获取不同数量的手牌
            int[] arrHandCards = table.getHandCard(player.getPlayerId());
            player.setHandCards(arrHandCards); //玩家看牌后则为手牌赋值
        }
        if (targetPlayer.getIsBlind() == 1) {
            //根据玩法的不同获取不同数量的手牌
            int[] arrHandCards = table.getHandCard(targetPlayer.getPlayerId());
            targetPlayer.setHandCards(arrHandCards); //玩家看牌后则为手牌赋值
        }
        //广播事件：通知全部客户端，用户已经完成了是否同意比牌的操作
        player.getHandCardTypeModel(table);
        targetPlayer.getHandCardTypeModel(table);

        PlayerInfo winner = null;
        if (table.getInGamePlayers().containsKey(player.getSeatNum())
                && table.getInGamePlayers().containsKey(targetPlayer.getSeatNum())) {
            winner = table.getWinnerByCompareCards(player, targetPlayer);
            //两人时为show牌 不能弃牌 否则结算会有玩家不亮手牌(因为策略是桌上只有一人不亮手牌)
            //修改玩家状态
            if (winner.getPlayerId().equals(player.getPlayerId())) {
                player.setState(PlayerStateEnum.gameing);
                targetPlayer.setState(PlayerStateEnum.showfail);
                table.getInGamePlayers().remove(targetPlayer.getSeatNum());
                if (targetPlayer.getIsBlind() != 1) {
                    table.setSeeCardNum(table.getSeeCardNum() - 1);
                }
            } else {
                player.setState(PlayerStateEnum.showfail);
                targetPlayer.setState(PlayerStateEnum.gameing);
                table.getInGamePlayers().remove(player.getSeatNum());
                if (player.getIsBlind() != 1) {
                    table.setSeeCardNum(table.getSeeCardNum() - 1);
                }
            }
        }
        return winner;
    }
    /**
     * 轮次到达上限的比牌逻辑
     *
     * @param table
     */
    public static void takeTurnsShowCD(final AbstractTable table, PlayerInfo player, PlayerInfo targetPlayer,PlayerInfo winner) {
        //停止牌桌内正在进行的倒计时
        TimerService.getInstance().delTimerTask(table.getRoomTableRelation());
        TimerService.getInstance().addTimerTask(4, table.getRoomTableRelation(), new TableInnerReq(table.getPlayType() + "", table.getRoomId(), table.getTableId()) {
            @Override
            public void processImpl() {
                NoticeBroadcastMessages.takeTurnsShow(table, player, targetPlayer);
                if (winner != null) {
                    if (winner.getPlayerId().equals(player.getPlayerId())) {
                        //广播事件：广播比牌大小的结果给桌内其它玩家
                        NoticeBroadcastMessages.sideShowResult(table, player, targetPlayer, null);
                    } else {
                        //广播事件：广播比牌大小的结果给桌内其它玩家
                        NoticeBroadcastMessages.sideShowResult(table, targetPlayer, player, null);
                    }
                } else {
                    //如果比牌时双方有人掉线则直接判定对方胜利
                    if (!table.getInGamePlayers().containsKey(player.getSeatNum())) {
                        //广播事件：广播比牌大小的结果给桌内其它玩家
                        NoticeBroadcastMessages.sideShowResult(table, targetPlayer, null, null);
                    } else if (!table.getInGamePlayers().containsKey(targetPlayer.getSeatNum())) {
                        //广播事件：广播比牌大小的结果给桌内其它玩家
                        NoticeBroadcastMessages.sideShowResult(table, player, null, null);
                    }
                }
                PlayerInfo nextActionPlayer = table.getShowPlayer(winner.getSeatNum());
                if (nextActionPlayer != null) {
                    //设置下一个行动玩家的状态
                    nextActionPlayer.setState(PlayerStateEnum.beting);
                    //如果超过底池上限则不发下一个行动玩家消息
                    if (table.getTotalAlreadyBet() >= table.getMaxPotScore()) {
                        nextActionPlayer = null;
                    }
                }
                GameLogic.specialShowTimer(table, 1, true, winner, nextActionPlayer);
            }
        });
    }

    /**
     * 结算牌局
     * 1、比较玩家手牌
     * 2、为赢家增加获胜的积分数量
     *
     * @param table
     */

    public static synchronized void settle(AbstractTable table) {
        long start = System.currentTimeMillis();
        try {
            //停止牌桌内正在进行的倒计时
            TimerService.getInstance().delTimerTask(table.getRoomTableRelation());
            log.debug("进入结算逻辑。tableState->" + table.getTableStateEnum() + ", tableInfo->" + TableUtil.toStringNormal(table));
            if (table.getTableStateEnum().getValue() == TableStateEnum.IDEL.getValue()) {
                log.debug("游戏处于空闲状态，无需执行结算逻辑。tableState->" + table.getTableStateEnum() + ", " + TableUtil.toStringNormal(table));
                return;
            }

            table.setTableStateEnum(TableStateEnum.SETTLE);
            table.setTableStatus();

            PlayerInfo winner = getWinner(table);
            //此处winScore是净胜
            double winScore = getWinScore(table, winner);
            //发送Notice通知给客户端
            try {
                NoticeBroadcastMessages.settleResult(table, winScore);
            } catch (Exception ex) {
                log.error("Send settle notice ERROR:" + ex.getMessage() + ", tableInfo->" + TableUtil.toStringNormal(table));
            }
            //当牌桌只剩一个玩家时，修改玩家状态为初始坐下状态
            if (table.getInGamePlayersBySeatNum().size() <= GameConst.FINAL_PLAYER_NUM) {
                TableService.getInstance().updateInGamePlayersBySeatNumPlayerStateSiteDown(table);
            }
            //StoredObjManager.set(RedisConst.GAME_STATUS_ACTIVE.getProfix() + Config.BIND_IP, "0");
            //StoredObjManager.setEx(RedisConst.GAME_STATUS_ACTIVE_SWITCH.getProfix() + Config.BIND_IP, "0", 5 * 60);
            log.debug("//调用计时器，倒计时结算动画" + table.getTableStateEnum() + ", tableInfo->" + TableUtil.toStringNormal(table));
            table.setLastActionTime(System.currentTimeMillis());
            //todo 此处可能需要定时器延时
            List<String> list = new ArrayList<>();
            table.getAllPlayers().forEach((k, v) -> {
                if (v.haveXiQian) {
                    list.add(k);
                }
            });
            //如果桌面有人可以获得喜钱则进入动画倒计时 否则直接进入结算
            if (list.size() != 0) {
                commonTimer(table, 1, "xiqian");
            } else {
                settleCD(table); //调用计时器，倒计时结算动画
            }
        }catch (Exception ex){
            log.error("结算 ERROR，msg->{}", ex.getMessage());
        }finally {
            if(System.currentTimeMillis() - start > 500){
                log.error("结算 执行时间 {} 毫秒", System.currentTimeMillis() - start);
            }
        }
    }

    //对需要结算的人进行排序
    private static List<Integer> getList(AbstractTable table) {
        List<Integer> list = new ArrayList<>();
        int seatNum = table.getCurrDealerSeatNum();
        while (true) {
            seatNum -= 1;
            if (seatNum < 1) {
                seatNum = 5;
            }
            if (table.getInGamePlayers().get(seatNum) != null) {
                list.add(seatNum);
            }
            if (seatNum == table.getCurrDealerSeatNum()) {
                break;
            }
        }
        log.info("当前比牌顺序：" + list.toString());
        return list;
    }

    //获取当前桌子的赢家
    public static PlayerInfo getWinner(AbstractTable table) {
        //找到剩余的没有弃牌的玩家
        PlayerInfo winner = null; //获胜的用户
//        List<Integer> list = getList(table);
        for (PlayerInfo player : table.getInGamePlayers().values()) {
//        for (Integer seatNum : list) {
//            PlayerInfo player = table.getInGamePlayers().get(seatNum);
//            if (player == null) {
//                continue;
//            }

            //如果用户已是旁观状态，并且此座位上还是该用户存在，那么清除此用户的游戏中信息
            PlayerInfo playerInfoBySeatNum = table.getInGamePlayersBySeatNum().get(player.getSeatNum());
            if (player.getState().getValue() == PlayerStateEnum.spectator.getValue()
                    && null != playerInfoBySeatNum
                    && playerInfoBySeatNum.getPlayerId().equals(player.getPlayerId())) {
                table.standUp(player.getSeatNum(), player.getPlayerId(), "Settle");
                if (player.getRoleType() != null && player.getRoleType().equals(RoleType.ROBOT)) {
                    LoggerUtils.robot.info("Robot standUp reason settle id:" + player.getPlayerId() + ",gameId:" + table.getPlayType()
                            + ",roomId:" + table.getRoomId() + ",tableId:" + table.getTableId());
                }
            }
            if (player.getState() == PlayerStateEnum.fold) {
                continue; //跳过已弃牌的玩家
            }
            if (player.getState() != PlayerStateEnum.fold && player.getHandCards() == null) {
                log.debug(player.playerToString() + " -> handCards is null: " + (player.getHandCards() == null));
                //如果用户无手牌数据，那么做发牌操作
                DealCardForTable oc1 = new DealCardForTable(table.getRoomTableRelation(), table.getCurrGameOrderId());
                int[] cards = oc1.hair_card(table.giveCardCounts(), player.getPlayerId());
                player.setHandCards(cards);
//                player.setIsBlind(2);
            }
            if (null == winner) {
                winner = player;
                player.getHandCardTypeModel(table);
            } else {
                //比较两个玩家的牌大小，留下赢家。
                if (player != null) {
                    PlayerInfo tmp = table.getWinnerByCompareCards(winner, player);
                    if (tmp.getPlayerId() == player.getPlayerId()) {
                        winner.getHandsWon().add(0);
                        winner.setChipsWon(0);
                        winner.setChipsLost(winner.getCurChipsLose());
                    } else {
                        player.getHandsWon().add(0);
                        player.setChipsWon(0);
                        player.setChipsLost(player.getCurChipsLose());
                    }
                    winner = tmp;
                }
            }
        }//for循环结束
        return winner;
    }

    //获取当前桌子结算赢家赢取的分数
    private static double getWinScore(AbstractTable table, PlayerInfo winner) {
        List<String> winUserIds = new ArrayList<>();
        //如果是系统开牌并有多个玩家牌型相同,则平分奖池
        int winnerNumber = 0;
        double serviceCharge = 0;
        if (table != null && table.getRoomConfig() != null) {
            serviceCharge = table.getRoomConfig().getServiceChargeRate();
        }

        double winScore = 0d;
        double totalAlreadyBet = table.getTotalAlreadyBet();
        if (winner != null) {
            //剔除玩家已下注值
            totalAlreadyBet -= winner.getTotalAlreadyBetScore4Hand();
            //如果玩家是孤注一掷赢得牌 则需要赔偿一部分钱给别的玩家
            if (winner.fullPressureShow > 0) {
                List<PlayerInfo> list = table.getFullPressure();
                long currBet = table.getCurrBaseBetScore();
                if (list.size() > 0) {
                    for(PlayerInfo player : list){
                        long A = player.getIsBlind() == 1 ? currBet / 2 : currBet;
                        long B = winner.fullPressureShow == 2 ? currBet / 2 : currBet;
                        double C = winner.getAlreadyBetScore4Round();
                        double score = A * (B - C) / B;
                        totalAlreadyBet -= score;
                        log.debug("自己下注的金额：" + winner.getTotalAlreadyBetScore4Hand() + " 当前赔偿人数：" + list.size());
                        log.debug("孤注一掷金额：A:" + A + " -> B:" + B + " -> C:" + C + " -> 返还：" + score);
                        player.addPlayScoreStore(score, LogReasons.CommonLogReason.FULLPRESSURESHOW);
                        NoticeBroadcastMessages.xiQian(table, player, score);
                        NoticeBroadcastMessages.moneyChangeNotice(table, player.getPlayerId(), player.getPlayScoreStore());
                    }
                }
            }

            //机器人杀量池添加
            RobotStrategyService.OBJ.addKillPool(totalAlreadyBet);

            double serviceFee = NumberUtils.double2Decimal(totalAlreadyBet * serviceCharge);
            winScore = NumberUtils.double2Decimal(totalAlreadyBet - serviceFee);
            log.debug("开始结算,赢家 uid={}, score={},servicefee={}", winner.getPlayerId(), winScore + winner.getTotalAlreadyBetScore4Hand(), serviceFee);

            winUserIds.add(winner.getPlayerId());
            //赢家积分数量更新
            winner.setWinner(true);
            winner.setChipsWon(table.getTotalAlreadyBet());
            if (winner.getBiggestChipsWon() < table.getTotalAlreadyBet()) {
                winner.setBiggestChipsWon(table.getTotalAlreadyBet());
            }
            winner.getHandsWon().add(1);
            winner.addPlayScoreStore(winScore + winner.getTotalAlreadyBetScore4Hand(), LogReasons.CommonLogReason.GAME_SETTLE);
            PlayerInfo playerInfo = table.getInGamePlayersBySeatNum().get(winner.getSeatNum());
            if (playerInfo != null) {
                playerInfo.setPlayScoreStore(winner.getPlayScoreStore());
            }
            //发送通知
            NoticeBroadcastMessages.moneyChangeNotice(table, winner.getPlayerId(), winner.getPlayScoreStore());
            LogService.OBJ.sendGameSettleLog(table, serviceFee, winner.getPlayerId());
            LogService.OBJ.sendLoseWinLog(table, winUserIds, winScore , winner.getTotalAlreadyBetScore4Hand());

            //根据需求，桌子上只剩一个玩家时，玩家获胜，不显示手牌
            if (table.getInGamePlayers().size() == 1 && table.getCurrRoundId() != table.getRoomConfig().getMaxBlindRound()) {
                winner.setHandCards(null);
            }
        } else {
            log.error("找不到赢家！！！");
        }
        TaskService.getInstance().winNum(table, winUserIds);
        TaskService.getInstance().winningStreakNum(table, winUserIds);

        return winScore;
    }

    /**
     * 结算动画倒计时
     *
     * @param table
     */
    public static void settleCD(final AbstractTable table) {
        //停止牌桌内正在进行的倒计时
        TimerService.getInstance().delTimerTask(table.getRoomTableRelation());
        log.debug("游戏结算后，准备开始下一局的倒计时，主要用于客户端展现结算动画。倒计时秒数->" + table.getCommonConfig().getGameStartCountDownSec());

        //region modify by gx 20181010 不再执行倒计时，而是游戏结算后，强制每个玩家离桌
        for (PlayerInfo player : table.getInGamePlayersBySeatNum().values()) {
            if (table.standUp(player.getSeatNum(), player.getPlayerId(), "SettleCD-mustLeave")) {
                if (player.getRoleType() != null && player.getRoleType().equals(RoleType.ROBOT)) {
                    LoggerUtils.robot.info("Robot standUp reason offline id:" + player.getPlayerId() + ",gameId:" + table.getPlayType()
                            + ",roomId:" + table.getRoomId() + ",tableId:" + table.getTableId());
                }
                //玩家游戏数据结算
                table.playerDataSettlement(player, false);
                table.returnLobby(player.getPlayerId(), false);
                log.info("一局游戏结束，结算结束，强制玩家离桌完成。 userId->{}. nickName->{}, gameId->{}, roomId->{}, tableId->{}",
                        player.getPlayerId(), player.getNickName(), table.getPlayType(), table.getRoomId(), table.getTableId());
            }

            //重置玩家游戏数据
            player.resetGameingInfo();
        }
        //修改桌子的状态
        table.setTableStateEnum(TableStateEnum.IDEL);
        table.setTableStatus();
        table.initTableStateAttribute();
        //endregion

        //结算与开局不连贯 暂时改成3秒
    //region 注释 by gx 20181009, 修改牌局逻辑，每一局后自动散桌，因此要清除桌内每个玩家的数据
//        TimerService.getInstance().addTimerTask(2, table.getRoomTableRelation(), new TableInnerReq(table.getPlayType() + "", table.getRoomId(), table.getTableId()) {
//            @Override
//            public void processImpl() {
//                for (PlayerInfo player : table.getInGamePlayersBySeatNum().values()) {
//                    if (player.isOffLine()) {
//                        //如果玩家已经掉线，那么强制站起
//                        if (table.standUp(player.getSeatNum(), player.getPlayerId(), "SettleCD-offLine")) {
//                            if (player.getRoleType() != null && player.getRoleType().equals(RoleType.ROBOT)) {
//                                LoggerUtils.robot.info("Robot standUp reason offline id:" + player.getPlayerId() + ",gameId:" + table.getPlayType()
//                                        + ",roomId:" + table.getRoomId() + ",tableId:" + table.getTableId());
//                            }
//                            //玩家游戏数据结算
//                            table.playerDataSettlement(player);
//                            table.returnLobby(player.getPlayerId());
//                            log.debug("玩家已掉线，已经强制站起并离开房间。" + player.playerToString());
//                        }
//                    } else if (player.getPlayScoreStore() < table.getBaseBetScore() / 2) {
//                        log.debug("余额不足玩家自动站起，玩家目前的钱：" + player.getPlayScoreStore() + "->桌子的底注：" + table.getBaseBetScore() / 2);
//                        //如果钱少了，那么强制站起
//                        if (table.standUp(player.getSeatNum(), player.getPlayerId(), "SettleCD-scoreError")) {
//                            if (player.getRoleType() != null && player.getRoleType().equals(RoleType.ROBOT)) {
//                                LoggerUtils.robot.info("Robot standUp reason money id:" + player.getPlayerId() + ",gameId:" + table.getPlayType()
//                                        + ",roomId:" + table.getRoomId() + ",tableId:" + table.getTableId());
//                            }
//                            //玩家游戏数据结算
//                            table.playerDataSettlement(player);
//                            table.returnLobby(player.getPlayerId());
//                        }
//                    }
//
//                    //重置玩家游戏数据
//                    player.resetGameingInfo();
//                }
//                //修改桌子的状态
//                table.setTableStateEnum(TableStateEnum.IDEL);
//                table.initTableStateAttribute();
//                TableService.getInstance().playGame(table);//重新开始游戏 注释by gx 20181009，修改牌局逻辑，每一局后自动散桌，并且不再自动开启新的一局
//            }
//        });
    //endregion
    }

    /**
     * 等待选择用户倒计时
     *
     * @param table
     */
    public static void waitChooseUserCD(final AbstractTable table, final PlayerInfo player) {
        //停止牌桌内正在进行的倒计时
        TimerService.getInstance().delTimerTask(table.getRoomTableRelation());
        log.debug("等待用户选择被比牌用户的倒计时，主要用于客户端展现等待动画。");
        TimerService.getInstance().addTimerTask(table.getCommonConfig().getBetCountDownSec(), table.getRoomTableRelation(), new TableInnerReq(table
                .getPlayType() + "", table.getRoomId(), table.getTableId()) {
            @Override
            public void processImpl() throws Exception {
                //如果超过底池上限则全部开牌
                if (table.getTotalAlreadyBet() >= table.getMaxPotScore()) {
                    //广播提示玩家总分超过桌子上线
                    NoticeBroadcastMessages.settlePrompt(table);
                    //调用定时器来延迟结算时间
                    GameLogic.commonTimer(table, 2, false);
                    return;
                }
                //如果倒计时结束用户没有选择比牌的人，做跟注处理
                PlayerInfo nextActionPlayer = table.getNextBetPlayer(player.getSeatNum(), NextBetPlayerReasonEnum.SIDE_SHOW_CHOOSE_USER);
                if (nextActionPlayer != null) {
                    if (table.getInGamePlayers().size() == 2 && table.getSeeCardNum() == 2) { //如果本局游戏只剩两人则可以show
                        nextActionPlayer.setIsCanShow(1);
                    }
                    nextActionPlayer.setState(PlayerStateEnum.beting);
                }
                int betSeqId = table.checkAndReturnBetSeqId();
                player.setSideshowOvertime(true);
                //广播玩家下注
                try {
                    NoticeBroadcastMessages.betRoundDoBet(table, betSeqId, player, nextActionPlayer, true);
                } catch (Exception ex) {
                    log.error("SendNotice ERROR：", ex);
                }

                //开始下一个玩家的下注倒计时
                betTimer(table);
            }
        });
    }

    /**
     * 等待用户是否允许比牌的倒计时
     *
     * @param table
     * @param player
     */
    public static void waitUserAllowSideShowCD(final AbstractTable table, final PlayerInfo player, final PlayerInfo targetPlayerInfo) {
        //停止牌桌内正在进行的倒计时
        TimerService.getInstance().delTimerTask(table.getRoomTableRelation());
        log.debug("等待用户是否允许比牌的倒计时，主要用于客户端展现等待动画。");
        TimerService.getInstance().addTimerTask(table.getCommonConfig().getAllowSideShowCountDownSec(), table.getRoomTableRelation(), new TableInnerReq(table.getPlayType() + "", table.getRoomId(), table.getTableId()) {
            @Override
            public void processImpl() throws Exception {
                //如果倒计时完成，但用户没有选择是否同意，那么默认为不同意比牌
                int isAllow = 0;
                //判断是否需要提示客户端展示下一个行动玩家的倒计时信息
                PlayerInfo nextActionPlayer = null;
                if (isAllow == 0) {
                    nextActionPlayer = table.getNextBetPlayer(player.getSeatNum(), NextBetPlayerReasonEnum.SIDE_SHOW_ALLOW);
                    //此处可能需要设置为行动状态 todo
                    if (nextActionPlayer != null) {
                        //设置下一个行动玩家的状态
                        nextActionPlayer.setState(PlayerStateEnum.beting);
                        if (table.getInGamePlayers().size() == 2 && table.getSeeCardNum() == 2) {
                            nextActionPlayer.setIsCanShow(1);
                        }
                    }
                }

                //广播事件：通知全部客户端，用户已经完成了是否同意比牌的操作
                NoticeBroadcastMessages.sideShowCompleteAllow(table, player, targetPlayerInfo, isAllow, nextActionPlayer);

                //用户不同意比牌，进入下一个行动用户的下注倒计时
                if (isAllow == 0) {
                    betTimer(table);
                }
            }
        });
    }

    /**
     * 通用计时器（show/发牌动画）
     *
     * @param table
     */
    public static void commonTimer(final AbstractTable table, Integer in, boolean flags) {
        //停止牌桌内正在进行的倒计时
        TimerService.getInstance().delTimerTask(table.getRoomTableRelation());
        TimerService.getInstance().addTimerTask(in, table.getRoomTableRelation(),
        new TableInnerReq(table.getPlayType() + "", table.getRoomId(), table.getTableId()) {
            @Override
            public void processImpl() {
                if (!flags) { //两秒为show得下注动画时间
                    settle(table);
                } else { //否则为发牌动画时间
                    log.debug("=====发牌动画===========" + in);
                    log.debug("第一次下注倒计时启动： time = {}", String.valueOf(System.currentTimeMillis()));
                    betTimer(table);//启动下注计时器
                }
            }
        });
    }

    /**
     * 特殊的比牌
     *
     * @param table
     */
    public static void specialShowTimer(final AbstractTable table, Integer in, boolean flags, PlayerInfo player, PlayerInfo targetPlayer) {
        //停止牌桌内正在进行的倒计时
        TimerService.getInstance().delTimerTask(table.getRoomTableRelation());
        TimerService.getInstance().addTimerTask(in, table.getRoomTableRelation(), new TableInnerReq(table.getPlayType() + "",
                table.getRoomId(), table.getTableId()) {
            @Override
            public void processImpl() {
                List<String> list = new ArrayList<>();
                list.add(player.getPlayerId());
                list.add(targetPlayer.getPlayerId());
                NoticeBroadcastMessages.takeTurnsShow(table,list);
            }
        });
    }

    /**
     * 乱起八糟的计时器(taskName：任务名称，后续多了做成enum)
     *
     * @param table
     */
    public static void commonTimer(final AbstractTable table, Integer in, String taskName) {
        //停止牌桌内正在进行的倒计时
        TimerService.getInstance().delTimerTask(table.getRoomTableRelation());
        TimerService.getInstance().addTimerTask(in, table.getRoomTableRelation(), new TableInnerReq(table.getPlayType() + "",
                table.getRoomId(), table.getTableId()) {
            @Override
            public void processImpl() {
                if ("xiqian".equals(taskName)) {
                    table.getAllPlayers().forEach((k, v) -> {
                        if (v.haveXiQian) {
                            v.addPlayScoreStore(table.getRoomConfig().getXiQian(), LogReasons.CommonLogReason.XIQIAN);
                            NoticeBroadcastMessages.xiQian(table, v, table.getRoomConfig().getXiQian());
                            NoticeBroadcastMessages.moneyChangeNotice(table, v.getPlayerId(), v.getPlayScoreStore());
                        }
                    });
                    settleCD(table); //调用计时器，倒计时结算动画
                }
            }
        });
    }

    public static void choosePlayTypeCD(final AbstractTable table) {
        //停止牌桌内正在进行的倒计时
        TimerService.getInstance().delTimerTask(table.getRoomTableRelation());
        TimerService.getInstance().addTimerTask(Config.CHOOSE_PLAYTYPE_CD, table.getRoomTableRelation(), new TableInnerReq(table.getPlayType() + "",
                table.getRoomId(), table.getTableId()) {
            @Override
            public void processImpl() {
                //自动选择上局玩法，没有则为classic
                table.setDealerPlayType(table.getDealerPlayType() != 0 ? table.getDealerPlayType() : PlayTypeEnum.classic.getGameId());
                table.choosePlayType(true);
            }
        });
    }
}
